package br.com.cpqd.ssar.posprocessamento;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.*;
import java.util.Vector;
//import javax.media.opengl.glu.GLU;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.*;
import org.lwjgl.opengl.glu.GLU;
import org.lwjgl.opengl.glu.*;
import org.lwjgl.input.Keyboard;

public class RenderizadorOff {

    private boolean done = false;
    private final String windowTitle = "Pós-Processamento";
    private DisplayMode displayMode;
    private float rotation = 0f;
    private float rotation2 = 0f;
    private float rotationAmount = .08f;
    Pbuffer pb;
    float pbufferScale = 2f;
    private boolean screen = true;
    private boolean bufferCapture;
    private int passo;
    private int op = 1; //opcoes para cada tipo de grafico
    private Vector list;
    private float eyeX = 12;
    private float eyeY = 12;
    private float eyeZ = 70;
    private float targetX = 12;
    private float targetY = 12;
    private float targetZ = 30;
    private boolean exit;
    private boolean rShift;
    private int listGrid;
    private String caminho;
//    public static void main(String args[]) {
//        RenderizadorOff app = new RenderizadorOff();
//        app.run();
//    }
    public void run(Vector vetElementos, int passo, String caminhoImg) {
        try {
            
           

            list = vetElementos;
            caminho = caminhoImg;
            this.passo = passo;

            init(); //cria buffer e outros elemtnos do OpenGL

            while (screen) {
                mainloop();
                GL11.glCallList(listGrid);
                renderOnDisplay();
                Display.update();
            }

            if (!exit) {
                renderOffDisplay();
            }
            cleanup();


        } catch (Exception e) {
            e.printStackTrace();
            System.exit(0);
        }
    }

    private void init() throws Exception {
        initDisplay();
        initGL(displayMode.getWidth(), displayMode.getHeight());

        pb = makePbuffer((int) (displayMode.getWidth() * pbufferScale),
                (int) (displayMode.getHeight() * pbufferScale));

        pb = selectPbuffer(pb);

        initGL(displayMode.getWidth(), displayMode.getHeight());

        selectDisplay();
    }

    private void initDisplay() throws Exception {

        DisplayMode d[] = Display.getAvailableDisplayModes();

        for (int i = 0; i < d.length; i++) {
            if (d[i].getWidth() == 800 //1024
                    && d[i].getHeight() == 600 //768
                    && d[i].getBitsPerPixel() == 32) {
                displayMode = d[i];
                break;
            }
        }

        Display.setDisplayMode(displayMode);
        Display.setTitle(windowTitle);

        Display.setFullscreen(false);
        Display.setVSyncEnabled(true);

        Display.create();
    }

    private void initGL(int displayWidth, int displayHeight) {

        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();


        org.lwjgl.opengl.glu.GLU.gluPerspective(
                60.0f,
                (float) displayWidth / (float) displayHeight,
                0.5f,
                1000.0f);


        GL11.glMatrixMode(GL11.GL_MODELVIEW);

        GL11.glEnable(GL11.GL_DEPTH_TEST);

        GL11.glClearColor(1.0f, 1.0f, 1.0f, 1);
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

        GL11.glEnable(GL11.GL_BLEND);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        geraGrid();

    }

    private void mainloop() {
        if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
            exit = true; //SAI DO SISTEMA
        }
        if (Display.isCloseRequested()) {
            exit = true;
        }

        handleKeyPressEvents();
    }

    public void keyDown(int keycode) {

        if (keycode == Keyboard.KEY_SYSRQ) {
            bufferCapture = true; //captura imagem da tela e salva em arquivo
        }

        if (keycode == Keyboard.KEY_F) {
            screen = false; //chama renderizacao da tela
        }

        if (keycode == Keyboard.KEY_ADD) {
            eyeZ -= 2;
        }

        if (keycode == Keyboard.KEY_SUBTRACT) {
            eyeZ += 2;
        }

        if (keycode == Keyboard.KEY_DOWN) {
            eyeY += 2;
            targetY += 2;

        }

        if (keycode == Keyboard.KEY_UP) {
            eyeY -= 2;
            targetY -= 2;
        }

        if (keycode == Keyboard.KEY_LEFT) {
            eyeX += 2;
            targetX += 2;
        }

        if (keycode == Keyboard.KEY_RIGHT) {
            eyeX -= 2;
            targetX -= 2;
        }

        if (keycode == Keyboard.KEY_W) {
            eyeX -= 2;
        }

        if (keycode == Keyboard.KEY_D) {
            eyeY -= 2;
        }

        if (keycode == Keyboard.KEY_A) {
            eyeZ -= 2;
        }

        if (keycode == Keyboard.KEY_S) {
            eyeZ -= 2;
        }

    }

    public void keyUp(int keycode) {
    }

    public void handleKeyPressEvents() {
        while (Keyboard.next()) {
            if (Keyboard.getEventKeyState()) {
                keyDown(Keyboard.getEventKey());
            } else {
                keyUp(Keyboard.getEventKey());
            }
        }
    }

    private void renderOffDisplay() throws FileNotFoundException, IOException {



        pb = selectPbuffer(pb);

        GL11.glLineWidth(pbufferScale);
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
        GL11.glPassThrough(GL11.GL_TRUE);
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glLoadIdentity();
         org.lwjgl.opengl.glu.GLU.gluLookAt(
                eyeX, eyeY, eyeZ, // posicao do olho
                targetX, targetY, targetZ, // posicao do objeto na cena
                0f, 1f, 0f);                    // posicao do vetor (retrato ou paisagem)

        CubeElement c = new CubeElement();
        for (int i = 0; i < list.size(); i++) {

            c = (CubeElement) list.get(i);
            if (c.getCor() != 0.0) {
                renderMovieFDTD3D(c);
            }

        }

        screenShot(pb.getWidth(), pb.getHeight(), caminho + "\\" + "movieFDTD3D_" + passo + ".png");



    }

    private void renderOnDisplay() throws FileNotFoundException, IOException {

        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glLoadIdentity();

         org.lwjgl.opengl.glu.GLU.gluLookAt(
                eyeX, eyeY, eyeZ,
                targetX, targetY, targetZ,
                0f, 1f, 0f);

        CubeElement c = new CubeElement(23.0, 23.0, 23.0, 0.8);
        renderMovieFDTD3D(c);
        c = new CubeElement(23.0, 23.0, 0.0, 0.8);
        renderMovieFDTD3D(c);
        c = new CubeElement(0.0, 23.0, 0.0, 0.8);
        renderMovieFDTD3D(c);
        c = new CubeElement(23.0, 23.0, 0.0, 0.8);
        renderMovieFDTD3D(c);
        c = new CubeElement(0.0, 0.0, 0.0, 0.8);
        renderMovieFDTD3D(c);
        c = new CubeElement(0.0, 23.0, 23.0, 0.8);
        renderMovieFDTD3D(c);
        c = new CubeElement(0.0, 0.0, 23.0, 0.8);
        renderMovieFDTD3D(c);
        c = new CubeElement(23.0, 0.0, 0.0, 0.8);
        renderMovieFDTD3D(c);
        c = new CubeElement(23.0, 0.0, 23.0, 0.8);
        renderMovieFDTD3D(c);

        if (bufferCapture) {

            screenShot(displayMode.getWidth(), displayMode.getHeight(), "screen_capture.png");

        }

    }

    private void renderMovieFDTD3D(CubeElement c) throws FileNotFoundException, IOException {

        // falta arrumar a versao de cores
        if (c.getCor() == 0.0) {
            GL11.glColor4d(0.0, 0.0, 0.1, 0.001);
        } else {
            if ((c.getCor() >= 0.0) && (c.getCor() <= 0.3)) {
                GL11.glColor4d(0.0, 0.0, c.getCor(), .8);
            } else if ((c.getCor() > 0.3) && (c.getCor() <= 0.6)) {
                GL11.glColor4d(0.0, c.getCor(), 0.0, .8);
            } else if ((c.getCor() > 0.6) && (c.getCor() <= 0.8)) {
                GL11.glColor4d(c.getCor(), 0.0, 0.0, .8);
            } else if (c.getCor() > 0.8) {
                GL11.glColor4d(c.getCor(), 0.0, 0.0, .8);
            }
        }

        GL11.glBegin(GL11.GL_QUADS);	// Face frontal
        GL11.glNormal3f(0f, 0f, 1f);  // Normal da face
        GL11.glVertex3d(c.getX() - 0.5, c.getY() + 0.5, c.getZ() + 0.5);
        GL11.glVertex3d(c.getX() + 0.5, c.getY() + 0.5, c.getZ() + 0.5);
        GL11.glVertex3d(c.getX() + 0.5, c.getY() - 0.5, c.getZ() + 0.5);
        GL11.glVertex3d(c.getX() - 0.5, c.getY() - 0.5, c.getZ() + 0.5);
        GL11.glEnd();

        GL11.glBegin(GL11.GL_QUADS);	// Face lateral direita
        GL11.glNormal3f(0f, 0f, -1f); // Normal da face
        GL11.glVertex3d(c.getX() + 0.5, c.getY() + 0.5, c.getZ() + 0.5);
        GL11.glVertex3d(c.getX() + 0.5, c.getY() + 0.5, c.getZ() - 0.5);
        GL11.glVertex3d(c.getX() + 0.5, c.getY() - 0.5, c.getZ() - 0.5);
        GL11.glVertex3d(c.getX() + 0.5, c.getY() - 0.5, c.getZ() + 0.5);
        GL11.glEnd();

        GL11.glBegin(GL11.GL_QUADS);	// Face de cima
        GL11.glNormal3f(-1f, 0f, 0f); // Normal da face
        GL11.glVertex3d(c.getX() - 0.5f, c.getY() + 0.5f, c.getZ() + 0.5f);
        GL11.glVertex3d(c.getX() - 0.5f, c.getY() + 0.5f, c.getZ() - 0.5f);
        GL11.glVertex3d(c.getX() + 0.5f, c.getY() + 0.5f, c.getZ() - 0.5f);
        GL11.glVertex3d(c.getX() + 0.5f, c.getY() + 0.5f, c.getZ() + 0.5f);
        GL11.glEnd();

        GL11.glBegin(GL11.GL_QUADS);	// Face lateral esquerda
        GL11.glNormal3f(1f, 0f, 0f);  // Normal da face
        GL11.glVertex3d(c.getX() - 0.5f, c.getY() + 0.5, c.getZ() + 0.5);
        GL11.glVertex3d(c.getX() - 0.5f, c.getY() + 0.5, c.getZ() - 0.5);
        GL11.glVertex3d(c.getX() - 0.5f, c.getY() - 0.5, c.getZ() - 0.5);
        GL11.glVertex3d(c.getX() - 0.5f, c.getY() - 0.5, c.getZ() + 0.5);
        GL11.glEnd();

        GL11.glBegin(GL11.GL_QUADS);	// Face trazeira
        GL11.glNormal3f(0f, 1f, 0f);  // Normal da face
        GL11.glVertex3d(c.getX() - 0.5, c.getY() + 0.5, c.getZ() - 0.5);
        GL11.glVertex3d(c.getX() + 0.5, c.getY() + 0.5, c.getZ() - 0.5);
        GL11.glVertex3d(c.getX() + 0.5, c.getY() - 0.5, c.getZ() - 0.5);
        GL11.glVertex3d(c.getX() - 0.5, c.getY() - 0.5, c.getZ() - 0.5);
        GL11.glEnd();

        GL11.glBegin(GL11.GL_QUADS);	// Face de baixo
        GL11.glNormal3f(0f, -1f, 0f); // Normal da face
        GL11.glVertex3d(c.getX() - 0.5, c.getY() - 0.5, c.getZ() + 0.5);
        GL11.glVertex3d(c.getX() - 0.5, c.getY() - 0.5, c.getZ() - 0.5);
        GL11.glVertex3d(c.getX() + 0.5, c.getY() - 0.5, c.getZ() - 0.5);
        GL11.glVertex3d(c.getX() + 0.5, c.getY() - 0.5, c.getZ() + 0.5);
        GL11.glEnd();

    }

    void geraGrid() {

        //lista para o grid
        listGrid = GL11.glGenLists(1);
        GL11.glNewList(listGrid, GL11.GL_COMPILE);
        GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
        GL11.glBegin(GL11.GL_QUADS);
        GL11.glColor3f(0.0f, 0.0f, 1.0f);
        for (int x = -300; x < 300; x = x + 50) {
            for (int y = -300; y < 300; y = y + 50) {
                GL11.glVertex2i(x, y);
                GL11.glVertex2i(x + 50, y);
                GL11.glVertex2i(x + 50, y + 50);
                GL11.glVertex2i(x, y + 50);
            }
        }
        GL11.glEnd();

        GL11.glColor3f(0.0f, 0.0f, 0.0f);
        GL11.glBegin(GL11.GL_LINES);
        GL11.glVertex3f(0, 0, 0);
        GL11.glVertex3f(5, 0, 0);
        GL11.glEnd();
        GL11.glRasterPos3f(4.5f, 0.5f, 0.5f);
        //glut.glutBitmapCharacter(GLUT.BITMAP_TIMES_ROMAN_10, 'X');

        GL11.glBegin(GL11.GL_LINES);
        GL11.glVertex3f(0, 0, 0);
        GL11.glVertex3f(0, 5, 0);
        GL11.glEnd();
        GL11.glRasterPos3f(0.5f, 4.5f, 0.5f);
        //glut.glutBitmapCharacter(GLUT.BITMAP_TIMES_ROMAN_10, 'Y');

        GL11.glBegin(GL11.GL_LINES);
        GL11.glVertex3f(0, 0, 0);
        GL11.glVertex3f(0, 0, 5);
        GL11.glEnd();
        GL11.glRasterPos3f(0.5f, 0.5f, 4.5f);
        // .glutBitmapCharacter(GLUT.BITMAP_TIMES_ROMAN_10, 'Z');
        GL11.glColor3f(1.0f, 1.0f, 1.0f);

        GL11.glEndList();
    }

    private void cleanup() {
        Display.destroy();
        if (pb != null) {
            pb.destroy();
        }
    }

    public Pbuffer makePbuffer(final int width, final int height) {
        Pbuffer pbuffer = null;
        try {
            pbuffer = new Pbuffer(width, height,
                    new PixelFormat(24, //bits por pixel
                    8,
                    24,
                    0,
                    0),
                    null,
                    null);
        } catch (LWJGLException e) {
            System.out.println("GLApp.makePbuffer(): exception " + e);
        }
        return pbuffer;
    }

    public Pbuffer selectPbuffer(Pbuffer pb) {
        if (pb != null) {
            try {
                //recria o buffer se necessário

                if (pb.isBufferLost()) {
                    int w = pb.getWidth();
                    int h = pb.getHeight();
                    System.out.println("GLApp.selectPbuffer(): Buffer contents lost - recreating the pbuffer");
                    pb.destroy();
                    pb = makePbuffer(w, h);
                }
                // seleciona o pbuffer para renderizar
                pb.makeCurrent();
            } catch (LWJGLException e) {
                System.out.println("GLApp.selectPbuffer(): exception " + e);
            }
        }
        return pb;
    }

    public void selectDisplay() {
        try {
            Display.makeCurrent();
        } catch (LWJGLException e) {
            System.out.println("GLApp.selectDisplay(): exception " + e);
        }
    }

    public static void frameSave(Pbuffer pbuff, int textureHandle) {
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureHandle);
        GL11.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, 0, 0, pbuff.getWidth(), pbuff.getHeight(), 0);
    }

    public static void screenShot(int width, int height, String saveFilename) {
        //alocando espaco para RGB

        ByteBuffer framebytes = allocBytes(width * height * 3);
        int[] pixels = new int[width * height];
        int bindex;

        GL11.glReadPixels(0, 0, width, height, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, framebytes);
        // copiando dados do RGB do ByteBuffer para um vetor de inteiros

        for (int i = 0; i < pixels.length; i++) {
            bindex = i * 3;
            pixels[i] =
                    0xFF000000 // A
                    | ((framebytes.get(bindex) & 0x000000FF) << 16) // R
                    | ((framebytes.get(bindex + 1) & 0x000000FF) << 8) // G
                    | ((framebytes.get(bindex + 2) & 0x000000FF) << 0);   // B
        }


        framebytes = null;

        pixels = GLImage.flipPixels(pixels, width, height);

        try {
            // criando um BufferedImage com os pixels RGB, e salvando como png

            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            image.setRGB(0, 0, width, height, pixels, 0, width);
            javax.imageio.ImageIO.write(image, "png", new File(saveFilename));
        } catch (Exception e) {
            System.out.println("screenShot(): exception " + e);
        }
    }
    public static final int SIZE_BYTE = 1;

    public static ByteBuffer allocBytes(int howmany) {
        return ByteBuffer.allocateDirect(howmany * SIZE_BYTE).order(ByteOrder.nativeOrder());
    }
}
